using System;
using NFreeMarker.Ext.Util;
using NFreeMarker.Template;

namespace NFreeMarker.Ext.Beans
{
    /**
     * <p>A class that will wrap an arbitrary array into {@link TemplateCollectionModel}
     * and {@link TemplateSequenceModel} interfaces. It supports element retrieval through the <tt>array[index]</tt>
     * syntax and can be iterated as a list.
     * @author Attila Szegedi
     * @version $Id: ArrayModel.java,v 1.26 2003/06/03 13:21:32 szegedia Exp $
     */
    public class ArrayModel : BeanModel, ITemplateCollectionModel, ITemplateSequenceModel
    {
        internal static readonly IModelFactory Factory = new ModelFactoryImpl();

        // Cached length of the array
        private readonly int _length;

        /**
         * Creates a new model that wraps the specified array object.
         * @param array the array object to wrap into a model.
         * @param wrapper the {@link BeansWrapper} associated with this model.
         * Every model has to have an associated {@link BeansWrapper} instance. The
         * model gains many attributes from its wrapper, including the caching 
         * behavior, method exposure level, method-over-item shadowing policy etc.
         * @throws IllegalArgumentException if the passed object is not a Java array.
         */
        public ArrayModel(object array, BeansWrapper wrapper)
            : base(array, wrapper)
        {
            Type clazz = array.GetType();
            if (!clazz.IsArray)
            {
                throw new ArgumentException("Object is not an array, it is " + array.GetType().FullName);
            }
            _length = ((Array) array).Length;
        }

        public ITemplateModelIterator Iterator()
        {
            return new IteratorImpl(this);
        }

        public ITemplateModel Get(int index)
        {
            try
            {
                return Wrap(((Array) Object).GetValue(index));
            }
            catch(IndexOutOfRangeException)
            {
                return null; 
    //            throw new TemplateModelException("Index out of bounds: " + index);
            }
        }

        private class IteratorImpl : ITemplateSequenceModel, ITemplateModelIterator
        {
            private readonly ArrayModel _arrayModel;
            private int _position;

            public IteratorImpl(ArrayModel arrayModel)
            {
                _arrayModel = arrayModel;
            }

            public bool HasNext()
            {
                return _position < _arrayModel._length;
            }

            public ITemplateModel Get(int index)
            {
                return _arrayModel.Get(index);
            }

            public ITemplateModel Next()
            {
                return _position < _arrayModel._length ? Get(_position++) : null;
            }

            public int Size() 
            {
                return _arrayModel.Size();
            }
        }

        public override int Size() 
        {
            return _length;
        }

        public override bool IsEmpty()
        {
            return _length == 0;
        }

        private class ModelFactoryImpl : IModelFactory
        {
            public ITemplateModel Create(object obj, IObjectWrapper wrapper)
            {
                return new ArrayModel(obj, (BeansWrapper) wrapper);
            }
        }
    }
}